﻿using System;
using System.IO;

using Moe.ECamel.Common.Entities;

namespace Moe.ECamel.Server.Services
{
	/// <summary>
	/// Base class for common service interface components.
	/// </summary>
	public abstract class ECamelService : IDisposable 
	{
		/// <summary>
		/// Reference to the service that invoked this service (null if this service is the root).
		/// </summary>
		private readonly ECamelService _caller;
		/// <summary>
		/// Lock object for instance locks.
		/// </summary>
		private readonly Object _lock;
		/// <summary>
		/// Reference to database connection to be used by read/write operations.
		/// </summary>
		private ECamelDataContext _dataContext;
		/// <summary>
		/// Reference to a database connection for read only operations.
		/// </summary>
		private ECamelDataContext _readOnlyDataContext;

		/// <summary>
		/// Constructor for root service interface.
		/// </summary>
		protected ECamelService()
			: this(null)
		{
		}
		/// <summary>
		/// Constructor for nested service interface.
		/// </summary>
		/// <param name="caller">The service which called this service.</param>
		protected ECamelService(ECamelService caller)
		{
			this._caller = caller;
			this._lock = caller != null ? caller._lock : new Object();
		}

		/// <summary>
		/// Creates an ECamel data context.
		/// </summary>
		/// <returns></returns>
		protected static ECamelDataContext CreateECamelDataContext(String path)
		{
			String connectionString = @"Data Source=.\SQLEXPRESS;Integrated Security=True;Initial Catalog=ECamel";
			if (!String.IsNullOrEmpty(path))
				connectionString += ";AttachDbFilename=" + Path.Combine(path, "ECamel.mdf");

			return new ECamelDataContext(connectionString);
		}

		/// <summary>
		/// Get a read-only connection
		/// </summary>
		protected ECamelDataContext GetReadOnlyDataContext()
		{
			// If not initialized yet, initialize:
			if (_readOnlyDataContext == null)
			{
				// Lock to protect from concurrent initializers:
				lock (_lock)
				{
					// Check if not initialized again to protect from races:
					if (_readOnlyDataContext == null)
					{
						if (_caller != null)
							_readOnlyDataContext = _caller.GetReadOnlyDataContext();
						else
						{
							// Create a new connection:
							_readOnlyDataContext = CreateECamelDataContext(null);
							// Optimize for readonly:
							_readOnlyDataContext.ObjectTrackingEnabled = false;
						}
					}
				}
			}
			return _readOnlyDataContext;
		}
		/// <summary>
		/// Get a read/write connection
		/// </summary>
		protected ECamelDataContext GetReadWriteDataContext()
		{
			// If not initialized yet, initialize:
			if (_dataContext == null)
			{
				// Lock to protect from concurrent initializers:
				lock (_lock)
				{
					// Check that context is still uninitialized, in case lock was approached simultanously:                                                                                                                                                                                                                                                                                                                                                                                                  n case an initializer was blocked:
					if (_dataContext == null)
					{
						// If this service was invoked by another service, reuse its data context, otherwise create a new one:
						if (_caller != null)
							_dataContext = _caller.GetReadWriteDataContext();
						else
							_dataContext = CreateECamelDataContext(null);
					}
				}
			}
			return _dataContext;
		}

		/// <summary>
		/// Submits changes unless there are errors.
		/// </summary>
		/// <param name="error"></param>
		/// <returns></returns>
		protected ECamelError SubmitChanges(ECamelError error)
		{
			if (error == null)
				SubmitChanges();
			return error;
		}
		/// <summary>
		/// Submits changes unless this service was called by another service.
		/// </summary>
		protected void SubmitChanges()
		{
			if (_caller == null)
				_dataContext.SubmitChanges();
		}

		/// <summary>
		/// Release resources used by this component.
		/// </summary>
		public virtual void Dispose()
		{
			if (_caller == null)
			{
				if (_dataContext != null)
				{
					_dataContext.Close();
					_dataContext = null;
				}
				if (_readOnlyDataContext != null)
				{
					_readOnlyDataContext.Close();
					_readOnlyDataContext = null;
				}
			}
		}
	}
} 